// LuoTaskTray.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

#include "include/LuoTaskTray.h"
#include "helper/UException.h"
#include "helper/UThreading.h"

#include "TaskTrayWindow.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

HINSTANCE g_hDLL;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call) {
	case DLL_PROCESS_ATTACH:
		g_hDLL = hModule;
		DisableThreadLibraryCalls(hModule);

		return TRUE;
	}

    return TRUE;
}

/* */
// TaskTrayModule
class TaskTrayModule :
	public ATL::CAtlDllModuleT<TaskTrayModule> {
public:
};

// TaskTrayModule
TaskTrayModule _AtlModule;


/* */
void*  UActivator_Create();

extern "C" HRESULT LUOTASKTRAY_FUNC STDCALL LuoTaskTray_Activate(LPCWSTR token, void ** obj)
{
	if (obj == 0) {
		return E_INVALIDARG;
	}

	*obj = 0;

	if (token == 0) {
		return E_INVALIDARG;
	}

	void*  new_obj = 0;

	if (lstrcmpW(token, L"activator") == 0) {
		new_obj = UActivator_Create();
	}

	if (new_obj == 0) {
		return E_NOINTERFACE;
	}

	*obj = new_obj;
	return S_OK;
}

// ActivatorImpl
class ActivatorImpl :
	public LuoTaskTray::UActivator,
	public LuoTaskTray::TaskTrayUtils,
	public LuoUtils::URunnable {
		HMODULE m_hRichMod;

		LuoUtils::Thread m_Thread;

		LuoUtils::CritSec m_CSec;

		LuoUtils::Event m_Event;

		typedef ATL::CAtlList<LuoTaskTray::XTopLevelWindow *> WindowList;

		WindowList m_Windows;

public:
	ActivatorImpl() :
	  m_hRichMod(0)
	  {
	  }

	  ~ActivatorImpl()
	  {
		  SafeRelease();
	  }

	  virtual HRESULT STDCALL Initialize()
	  {
		  m_hRichMod = LoadLibrary(WTL::CRichEditCtrl::GetLibraryName());
		  if (m_hRichMod == 0) {
			  return E_OUTOFMEMORY;
		  }

		 HRESULT  hRslt = m_Event.Create(true);
		  if (FAILED(hRslt)) {
			  return hRslt;
		  }

		  try {
			  m_Thread.SetupName("LuoTaskTray");
			  m_Thread.Create(this);
		  }
		  catch(LuoUtils::Exception &) {
			  return E_FAIL;
		  }

		  DWORD result = WaitForSingleObject(m_Event, 3000);
		  if (result != WAIT_OBJECT_0) {
			  return E_FAIL;
		  }

		  return S_OK;
	  }

	  virtual void STDCALL Uninitialize()
	  {
		  SafeRelease();
	  }

	  /* */
	  void SafeRelease()
	  {
		  DWORD tid = m_Thread.GetThreadId();
		  for (int i = 0; i < 3; i++) {
			  if (i > 0) {
				  ::Sleep(100);
			  }

			  if (PostThreadMessage(tid, WM_QUIT, 0, 0)) {
				  break;
			  }
		  }

		  HANDLE hThread = m_Thread;
		  if (hThread != 0) {
			  HANDLE handles[] = {
				  hThread
			  };

			  for (;;) {
				  DWORD dwResult = ::MsgWaitForMultipleObjects(1, handles, FALSE, 50, QS_ALLINPUT);

				  if (dwResult == WAIT_TIMEOUT) {
					  tid = m_Thread.GetThreadId();
					  for (int i = 0; i < 3; i++) {
						  if (i > 0) {
							  ::Sleep(100);
						  }

						  if (PostThreadMessage(tid, WM_QUIT, 0, 0)) {
							  break;
						  }
					  }

					  continue;
				  }
				  else if (dwResult == WAIT_OBJECT_0) {
					  break;
				  }

				  MSG msg = { 0};

				  BOOL fPeek = ::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE);
				  if (fPeek) {
					  ::TranslateMessage(&msg);
					  ::DispatchMessageW(&msg);
				  }
			  }
		  }

		  m_Thread.Join();
		  m_Thread.Close();

		  if (m_hRichMod != 0) {
			  FreeLibrary(m_hRichMod);
			  m_hRichMod = 0;
		  }
	  }

	  /* */
	  virtual int STDCALL Run()
	  {
		  HRESULT hRslt = ::OleInitialize(0);
		  if (FAILED(hRslt)) {
			  return -1;
		  }

		  int result = RunImpl();

		  ::OleUninitialize();

		  return result;
	  }

	  int RunImpl()
	  {
		  // Prepare Windows Message Queue
		  MSG msg = { 0};
		  BOOL fPeek = ::PeekMessageW(&msg, 0, 0, 0, PM_NOREMOVE);

		  m_Event.SetEvent();

		  int result = DoProcess();

		  return result;
	  }

	  int DoProcess()
	  {
		  int result = -1;

		  for (;;) {
			  MSG msg = { 0};

			  BOOL fPeek = ::PeekMessageW(&msg, 0, 0, 0, PM_REMOVE);
			  if (fPeek) {
				  if (msg.message == WM_QUIT) {
					  result = (int)msg.wParam;
					  break;
				  }

				  if (msg.message == LuoTaskTray::WM_CREATE_TASK_TRAY) {
					  LuoTaskTray::UCreateParam * p = (LuoTaskTray::UCreateParam *) msg.lParam;
					  if (p != 0) {
						  CreateTaskTrayImpl(p);
						  Delete(p);
					  }

					  continue;
				  }

				  if (!IsPreprocessWindowMessage(&msg)) {
					  ::TranslateMessage(&msg);
					  ::DispatchMessageW(&msg);
				  }
			  }
			  else {
				  WaitMessage();
			  }
		  }

		  RemoveAllTopLevelWindows();

		  return result;
	  }

	  bool IsPreprocessWindowMessage(MSG* msg)
	  {
		  POSITION pos = m_Windows.GetHeadPosition();
		  while (pos != 0) {
			  LuoTaskTray::XTopLevelWindow * window = m_Windows.GetNext(pos);
			  if (window != 0 && window->PreprocessWindowMessage(msg)) {
				  return true;
			  }
		  }

		  return false;
	  }

	  virtual void AddTopLevelWindow(LuoTaskTray::XTopLevelWindow * window)
	  {
		  POSITION pos = m_Windows.GetHeadPosition();
		  while (pos != 0) {
			  LuoTaskTray::XTopLevelWindow * hItem = m_Windows.GetNext(pos);
			  if (window == hItem) {
				  return;
			  }
		  }

		  m_Windows.AddTail(window);
	  }

	  virtual void RemoveTopLevelWindow(LuoTaskTray::XTopLevelWindow * window)
	  {
		  POSITION pos = m_Windows.Find(window);
		  if (pos != 0) {
			  m_Windows.RemoveAt(pos);
		  }
	  }

	  /* */
	  void RemoveAllTopLevelWindows()
	  {
		  while (!m_Windows.IsEmpty()) {
			  LuoTaskTray::XTopLevelWindow * window = m_Windows.RemoveTail();
			  if (window != 0) {
				  window->FinalRelease();
			  }
		  }
	  }

	  /* */
	  static LuoTaskTray::UCreateParam *
		  Copy(LuoTaskTray::UCreateParam * src) {
			  LuoTaskTray::UCreateParam * dst = static_cast<LuoTaskTray::UCreateParam*>(malloc(sizeof(LuoTaskTray::UCreateParam)));

			  dst->hwnd = src->hwnd;
			  dst->CallbackMessage = src->CallbackMessage;

			  return dst;
	  }

	  /* */
	  static void Delete(LuoTaskTray::UCreateParam * p)
	  {
		  if (p) {
			  free(p);
			  p = NULL;
		  }
	  }

	  /* */
	  virtual HRESULT STDCALL CreateTaskTray(LuoTaskTray::UCreateParam * param)
	  {
		  if (param == 0) {
			  return E_INVALIDARG;
		  }

		  LuoTaskTray::UCreateParam * p = Copy(param);

		  DWORD tid = m_Thread.GetThreadId();
		  if (!PostThreadMessage(tid, LuoTaskTray::WM_CREATE_TASK_TRAY, 0, (LPARAM) p)) {
			  Delete(p);
			  return E_FAIL;
		  }

		  return S_OK;
	  }

	  /* */
	  void CreateTaskTrayImpl(LuoTaskTray::UCreateParam * p)
	  {
		  TaskTrayWindow*  dummyWindow = TaskTrayWindow::CreateFrame(p);
		  if (dummyWindow) {
			  AddTopLevelWindow(dummyWindow);
		  }
	  }
};

LuoTaskTray::TaskTrayUtils * LuoTaskTray::g_pUtils;

// ActivatorImpl
void* UActivator_Create()
{
	static ActivatorImpl impl;

	if (LuoTaskTray::g_pUtils == 0) {
		LuoTaskTray::g_pUtils = &impl;
	}

	return static_cast<LuoTaskTray::UActivator*>(&impl);
}

/* */


#ifdef _MANAGED
#pragma managed(pop)
#endif

